package free.order.security;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.acegisecurity.ConfigAttributeDefinition;
import org.acegisecurity.ConfigAttributeEditor;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.intercept.method.MethodDefinitionSource;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.CodeSignature;
import org.springframework.util.Assert;

import free.order.security.cache.AcegiCacheManager;
import free.order.security.resource.ResourceDetails;


/**
 * 在resourceCache中保存每个方法对应的ResourceDetails
 *
 * @author cac
 */
public class DBMethodDefinitionSource implements MethodDefinitionSource {
	private static final Log logger = LogFactory.getLog(DBMethodDefinitionSource.class);

	private AcegiCacheManager acegiCacheManager;

	public void setAcegiCacheManager(AcegiCacheManager acegiCacheManager) {
		this.acegiCacheManager = acegiCacheManager;
	}

	// ~ Methods
	// ========================================================================================================

	public ConfigAttributeDefinition getAttributes(Object object) {
		Assert.notNull(object, "Object cannot be null");

		if (object instanceof MethodInvocation) {
			MethodInvocation miv = (MethodInvocation) object;
			return this.lookupAttributes(miv.getThis().getClass(), miv.getMethod());
		}

		if (object instanceof JoinPoint) {
			JoinPoint jp = (JoinPoint) object;
			Class targetClazz = jp.getTarget().getClass();
			String targetMethodName = jp.getStaticPart().getSignature().getName();
			Class[] types = ((CodeSignature) jp.getStaticPart().getSignature()).getParameterTypes();

			if (logger.isDebugEnabled()) {
				logger.debug("Target Class: " + targetClazz);
				logger.debug("Target Method Name: " + targetMethodName);

				for (int i = 0; i < types.length; i++) {
					logger.debug("Target Method Arg #" + i + ": " + types[i]);
				}
			}

			try {
				return this.lookupAttributes(targetClazz, targetClazz.getMethod(targetMethodName, types));
			} catch (NoSuchMethodException nsme) {
				throw new IllegalArgumentException("Could not obtain target method from JoinPoint: " + jp, nsme);
			}
		}

		throw new IllegalArgumentException("Object must be a MethodInvocation or JoinPoint");
	}

	public boolean supports(Class clazz) {
		return (MethodInvocation.class.isAssignableFrom(clazz) || JoinPoint.class.isAssignableFrom(clazz));
	}

	/**
	 * 从resourceCache中获取当前方法对应的ResourceDetails{@link ResourceDetails} 最后返回由Role Name
	 * 组装成的ConfigAttributeDefinition(@link ConfigAttributeDefinition)
	 *
	 * @see org.acegisecurity.intercept.method.AbstractMethodDefinitionSource#lookupAttributes(java.lang.reflect.Method)
	 */
	@SuppressWarnings("unchecked")
	protected ConfigAttributeDefinition lookupAttributes(Class clszz, Method mi) {
		Assert.notNull(mi, "lookupAttrubutes in the DBMethodDefinitionSource is null");
		acegiCacheManager.initResourceCache();

		// 获取所有的function
		List methodStrings = acegiCacheManager.getFunctions();

		Set auths = new HashSet();
		// 取权限的合集
		for (Iterator iter = methodStrings.iterator(); iter.hasNext();) {
			String methodString = (String) iter.next();
			if (isMatch(clszz, mi, methodString)) {
				ResourceDetails resourceDetails = acegiCacheManager.getAuthorityFromCache(methodString);
				if (resourceDetails == null) {
					break;
				}
				GrantedAuthority[] authorities = resourceDetails.getAuthorities();
				if (authorities == null || authorities.length == 0) {
					break;
				}
				auths.addAll(Arrays.asList(authorities));
			}
		}

		if (auths.size() == 0)
			return null;

		ConfigAttributeEditor configAttrEditor = new ConfigAttributeEditor();
		String authoritiesStr = " ";

		for (Iterator iter = auths.iterator(); iter.hasNext();) {
			GrantedAuthority authority = (GrantedAuthority) iter.next();
			authoritiesStr += authority.getAuthority() + ",";
		}

		String authStr = authoritiesStr.substring(0, authoritiesStr.length() - 1);

		configAttrEditor.setAsText(authStr);
		return (ConfigAttributeDefinition) configAttrEditor.getValue();
	}

	public Iterator getConfigAttributeDefinitions() {
		return null;
	}

	/**
	 * Return if the given method name matches the mapped name. The default implementation checks for "xxx" and "xxx"
	 * matches.
	 */
	public static boolean isMatch(Class clszz, Method mi, String methodString) {
		boolean isMatch = true;
		try {
			int lastDotIndex = methodString.lastIndexOf('.');
			String className = methodString.substring(0, lastDotIndex);
			String methodName = methodString.substring(lastDotIndex + 1);

			// 判断类是否相等
			if (!clszz.getName().equals(className))
				isMatch = false;

			// 判断接口是否相等
			Class[] interfaces = clszz.getInterfaces();
			for (int i = 0; i < interfaces.length; i++) {
				Class inf = interfaces[i];
				if (inf.getName().equals(className)) {
					isMatch = true;
				}
			}

			// 判断方法是否相等
			if (isMatch
					&& !(mi.getName().equals(methodName)
					|| (methodName.endsWith("*") && mi.getName().startsWith(
					methodName.substring(0, methodName.length() - 1))) || (methodName.startsWith("*") && mi
					.getName().endsWith(methodName.substring(1, methodName.length())))))
				isMatch = false;

		} catch (Exception e) {
			isMatch = false;
		}
		return isMatch;
	}

}
